package com.sap.apitest2.service;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import net.sf.ezmorph.Morpher;
import net.sf.ezmorph.object.IdentityObjectMorpher;
import net.sf.json.JSONArray;
import net.sf.json.JSONFunction;
import net.sf.json.JSONNull;
import net.sf.json.JSONObject;
import net.sf.json.util.JSONUtils;

import com.sap.apitest2.entity.Result;

public class JSonResultEvaluator extends ResultEvaluator {

	protected boolean evaluateNLocateMismatchByFormat(Result result) {
		logger.info("evaluateNLocateMismatchByFormat: JSON");
		String exp = result.getExpectedResponse();
		String act = result.getResponse();
		JSONObject joExp = JSONObject.fromObject(exp);
		JSONObject joAct = JSONObject.fromObject(act);
		boolean same = compareJSONs(joExp, joAct);
		if (!same) {
			result.setExpectedResponse(joExp.toString());
			result.setResponse(joAct.toString());
		}
		logger.info("same=" + same);
		return !same;
	}

	private static String markString(Object i_obj) {
		return "<@@@" + String.valueOf(i_obj) + "@@@>";
	}

	private static boolean compareJSONs(Object i_objA, Object i_objB) {
		if (i_objA == i_objB) {
			return true;
		}
		if (i_objA == null || i_objB == null) {
			return false;
		}
		
		if (i_objA instanceof JSONObject && i_objB instanceof JSONObject) {
			JSONObject joExp = (JSONObject)i_objA;
			JSONObject joAct = (JSONObject)i_objB;
			return compareJSONObject(joExp, joAct);
		} else if (i_objA instanceof JSONArray && i_objB instanceof JSONArray) {
			JSONArray jaExp = (JSONArray)i_objA;
			JSONArray jaAct = (JSONArray)i_objB;
			return compareJSONArray(jaExp, jaAct);
		} else {
			if (i_objA instanceof String) {
				if (!i_objA.equals(String.valueOf(i_objB))) {
					return false;
				}
			} else if (i_objB instanceof String) {
				if (!i_objB.equals(String.valueOf(i_objA))) {
					return false;
				}
			} else {
				Morpher m1 = JSONUtils.getMorpherRegistry().getMorpherFor(i_objA.getClass());
				Morpher m2 = JSONUtils.getMorpherRegistry().getMorpherFor(i_objB.getClass());
				boolean checked = false;
				if ( ( m1 != null && m1 != IdentityObjectMorpher.getInstance() ) ) {
					if (!i_objA.equals(JSONUtils.getMorpherRegistry().morph(i_objA.getClass(), i_objB))) {
						return false;
					}
					checked = true;
				}
				
				if (m2 != null && m2 != IdentityObjectMorpher.getInstance()) {
					if (!JSONUtils.getMorpherRegistry().morph(i_objB.getClass(), i_objA).equals(i_objB)) {
						return false;
					}
					checked = true;
				} 

				if (!checked){
					if (!i_objA.equals(i_objB)) {
						return false;
					}
				}
			}
		}
		return true;
	}

	private static boolean compareJSONObject(JSONObject joExp, JSONObject joAct) {
		if (joExp.isNullObject()) {
			if (joAct.isNullObject()) {
				return true;
			} else {
				return false;
			}
		} else {
			if (joAct.isNullObject()) {
				return false;
			}
		}
		
		JSONObject joBigger = joExp.size() > joAct.size() ? joExp : joAct;
		JSONObject joSmaller = joExp.size() > joAct.size() ? joAct : joExp;
		
		for (Iterator keys = joBigger.keySet().iterator(); keys.hasNext();) {
			String key = (String) keys.next();
			Object o1 = joBigger.get(key);
			if (!joSmaller.containsKey(key)) {
				String val = markString(o1);
				joBigger.put(key, val);
				return false;
			}
			Object o2 = joSmaller.get(key);

			if (JSONNull.getInstance().equals(o1)) {
				if (JSONNull.getInstance().equals(o2)) {
					continue;
				} else {
					return false;
				}
			} else {
				if (JSONNull.getInstance().equals(o2)) {
					return false;
				}
			}
			
			boolean res = compareJSONs(o1, o2);
			
			if (!res) {
				if ( !(o1 instanceof JSONObject && o2 instanceof JSONObject)
						&& !(o1 instanceof JSONArray && o2 instanceof JSONArray)
						) {
					joExp.put(key, markString(o1));
					joAct.put(key, markString(o2));
				} 
				return false;
			}
		}
		return true;
	}
	
	private static boolean compareJSONArray(JSONArray ja1, JSONArray ja2) {
		JSONArray jaBigger = ja1.size() > ja2.size() ? ja1 : ja2;
		JSONArray jaSmaller = ja1.size() > ja2.size() ? ja2 : ja1;
		for (int j = 0; j < jaBigger.size(); j++) {
			Object oBigger = jaBigger.get(j);
			if (j >= jaSmaller.size()) {
				jaBigger.set(j, markString(oBigger));
				continue;
			}
			Object oSmaller = jaSmaller.get(j);
			
			boolean res = compareJSONs(oBigger, oSmaller);
			
			if (!res) {
				if ( !(oBigger instanceof JSONObject && oSmaller instanceof JSONObject)
						&& !(oBigger instanceof JSONArray && oSmaller instanceof JSONArray)
						) {
					jaBigger.set(j, markString(oBigger));
					jaSmaller.set(j, markString(oSmaller));
				} 
				return false;
			}
		}
		return true;
	}

	public static void main(String[] argv) {
		Result result = new Result();
		StringBuffer sbAct = new StringBuffer();
		StringBuffer sbExp = new StringBuffer();
		try {
			FileReader frAct = new FileReader("C:\\apitest_act_json.txt");
			BufferedReader brAct = new BufferedReader(frAct);
			String line;
			while ((line = brAct.readLine()) != null) {
				sbAct.append(line).append('\n');
			}
			FileReader frExp = new FileReader("C:\\apitest_exp_json.txt");
			BufferedReader brExp = new BufferedReader(frExp);
			while ((line = brExp.readLine()) != null) {
				sbExp.append(line).append('\n');
			}
		} catch (Exception e) {
			System.out.println(e);
		}
		result.setResponse(sbAct.toString());
		result.setExpectedResponse(sbExp.toString());
		ResultEvaluator evaluator = new JSonResultEvaluator();
		evaluator.evaluateNLocateMismatchByFormat(result);
	}
}
